﻿// Portions copyright 2006, 2007 - Rory Plaire (codekaizen@gmail.com)
// Portions copyright 2007, 2008 - Ariel Yaroshevich (a.k.a blackrussian) (hamlet@inter.net.il)
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpMap.Rendering.Rendering2D;
using WpfPoint = System.Windows.Point;
using WpfSize = System.Windows.Size;
using WpfRectangle = System.Windows.Rect;
using WpfPen = System.Windows.Media.Pen;
using WpfBrush = System.Windows.Media.Brush;
using WpfBrushes = System.Windows.Media.Brushes;
using WpfFont = System.Windows.Media.Typeface;
using WpfFontFamily = System.Windows.Media.FontFamily;
using WpfFontStyle = System.Windows.FontStyle;
using WpfColor = System.Windows.Media.Color;
using WpfStreamPath = System.Windows.Media.StreamGeometry;
using WpfPath = System.Windows.Media.PathGeometry;
using WpfPathFigure = System.Windows.Media.PathFigure;
using WpfMatrix = System.Windows.Media.Matrix;
using WpfDrawing = System.Windows.Media.Drawing;
using WpfLineSegment = System.Windows.Media.LineSegment;
using StyleColorMatrix = SharpMap.Rendering.ColorMatrix;
using WpfImageSource = System.Windows.Media.ImageSource;
using WpfGeometryDrawing = System.Windows.Media.GeometryDrawing;
using System.Threading;
using SharpMap.Styles;
using SharpMap.Rendering;
using System.Windows.Media;

namespace SharpMap.Wpf.Rendering
{
    public class VectorRenderer :  VectorRenderer2D<Drawing>
    {
              
        #region fields
        Dictionary<Path2D, WpfStreamPath> _pathCache = new Dictionary<Path2D, WpfStreamPath>();
        private static readonly ReaderWriterLockSlim _pathCacheLock = new ReaderWriterLockSlim();
        #endregion

        #region VectorRenderer2D<Drawing>

        public override IEnumerable<Drawing> RenderPaths(IEnumerable<Path2D> paths,
                                                                   StylePen outline,
                                                                    StylePen highlightOutline,
                                                                        StylePen selectOutline,
                                                                            RenderState renderState)
        {
            IEnumerable<WpfStreamPath> wpfPaths = CreatePaths(paths, false, true);
            foreach(WpfStreamPath path in wpfPaths)
            {
                GeometryDrawing renderObj = new GeometryDrawing();
                switch (renderState)
                {
                    case RenderState.Normal:
                        renderObj = new GeometryDrawing(null,
                            ViewConverter.Convert(outline),
                            path);
                        break;
                    case RenderState.Selected:
                        renderObj = new GeometryDrawing(null,
                            ViewConverter.Convert(selectOutline),
                            path);
                        break;
                    case RenderState.Highlighted:
                        renderObj = new GeometryDrawing(
                            null,
                            ViewConverter.Convert(highlightOutline),
                            path);
                        break;
                    case RenderState.Unknown:
                    default:
                        throw new ArgumentException("Unknown render state", "renderState");
                }
                renderObj.Geometry = path;
 
                yield return renderObj;
            }

        }


        public override IEnumerable<Drawing> RenderPaths(IEnumerable<Path2D> paths,
                                                                StyleBrush fill,
                                                                StyleBrush highlightFill,
                                                                   StyleBrush selectFill,
                                                                   StylePen outline,
                                                                    StylePen highlightOutline,
                                                                    StylePen selectOutline,
                                                                     RenderState renderState)
        {
            IEnumerable<WpfStreamPath> wpfPaths = CreatePaths(paths, true, outline != null);
            foreach(WpfStreamPath path in wpfPaths)
            {
                GeometryDrawing renderObj = null;
                switch (renderState)
                {
                    case RenderState.Normal:
                        renderObj = new GeometryDrawing(ViewConverter.Convert(fill), 
                            ViewConverter.Convert(outline),
                            path);
                        break;
                    case RenderState.Selected:
                        renderObj = new GeometryDrawing(ViewConverter.Convert(selectFill), 
                            ViewConverter.Convert(selectOutline),
                            path);
                        break;
                    case RenderState.Highlighted:
                        renderObj = new GeometryDrawing(
                            ViewConverter.Convert(highlightFill), 
                            ViewConverter.Convert(highlightOutline),
                            path);
                        break;
                    case RenderState.Unknown:
                    default:
                        throw new ArgumentException("Unknown render state", "renderState");
                }

                renderObj.Geometry = path;
                yield return renderObj;
            }        
        }


        public override IEnumerable<Drawing> RenderPaths(IEnumerable<Path2D> paths,
                                                          StylePen line, StylePen highlightLine,
                                                          StylePen selectLine,
                                                          StylePen outline, StylePen highlightOutline,
                                                          StylePen selectOutline,
                                                             RenderState renderState)
        {
            System.Boolean stroked = line != null || outline != null;
            IEnumerable<WpfStreamPath> wpfPaths = CreatePaths(paths, false, stroked);
            foreach (WpfStreamPath path in wpfPaths)
            {
                DrawingGroup renderObj = new DrawingGroup();
                StylePen effectiveLineStyle = line;
                StylePen effectiveOutlineStyle = outline;
           
                switch (renderState)
                {
                    case RenderState.Normal:
                        //done above
                        break;
                    case RenderState.Selected:
                        effectiveLineStyle = selectLine ?? line;
                        effectiveOutlineStyle = selectOutline ?? outline;
                        break;
                    case RenderState.Highlighted:
                        effectiveLineStyle = highlightLine ?? line;
                        effectiveOutlineStyle = highlightOutline ?? outline;
                        break;
                    case RenderState.Unknown:
                    default:
                        throw new ArgumentException("Unknown render state", "renderState");
                }

                GeometryDrawing lineGeometry = new GeometryDrawing(null, ViewConverter.Convert(effectiveLineStyle), path);
                GeometryDrawing outlineGeometry = new GeometryDrawing(null, ViewConverter.Convert(effectiveOutlineStyle), path);
                renderObj.Children.Add(lineGeometry);
                renderObj.Children.Add(outlineGeometry);
                yield return renderObj;
            }
        }

        public override IEnumerable<Drawing> RenderSymbols(IEnumerable<Point2D> locations, Symbol2D symbolData, RenderState renderState)
        {
            return RenderSymbols(locations, symbolData, symbolData, symbolData, renderState);
        }

        public override IEnumerable<Drawing> RenderSymbols(IEnumerable<Point2D> locations, Symbol2D symbolData
            , ColorMatrix highlight, ColorMatrix select, RenderState renderState)
        {
            throw new NotSupportedException();
        }

        public override IEnumerable<Drawing> RenderSymbols(IEnumerable<Point2D> locations, Symbol2D symbolData
            , Symbol2D highlightSymbolData, Symbol2D selectSymbolData, RenderState renderState)
        {

  
            Symbol2D effectiveSymbol;
            switch (renderState)
            {
                case RenderState.Unknown:
                    effectiveSymbol = symbolData;
                    break;
                case RenderState.Normal:
                    effectiveSymbol = symbolData;
                    break;
                case RenderState.Selected:
                    effectiveSymbol = selectSymbolData;
                    break;
                case RenderState.Highlighted:
                    effectiveSymbol = highlightSymbolData;
                    break;
                default:
                    effectiveSymbol = symbolData;
                    break;
            }
            
            //TODO: handle passed in SharpMap.Style
            foreach (Point2D location in locations)
            {
                System.Windows.Media.ImageDrawing imageDrawing = new ImageDrawing();
                WpfSymbol2D wpfSymbol = effectiveSymbol as WpfSymbol2D;
                if (wpfSymbol != null)
                {
                    imageDrawing.ImageSource = wpfSymbol.ImageSource;
                }
                else 
                {
                    imageDrawing.ImageSource = new System.Windows.Media.DrawingImage(); 
                }
                imageDrawing.Rect = new System.Windows.Rect(location.X, location.Y, symbolData.Size.Width, symbolData.Size.Height);
                yield return imageDrawing;
            }
            yield break;
        }
        #endregion

        


        private IEnumerable<WpfStreamPath> CreatePaths(IEnumerable<Path2D> paths,
                                                    System.Boolean filled, System.Boolean stroked)
        {

            filled = true;
            stroked = true;
            foreach (Path2D path in paths)
            {
                WpfStreamPath pathCache;


                if (!TryGetCache(path, out pathCache))
                {

                    pathCache = new WpfStreamPath();
                    using (System.Windows.Media.StreamGeometryContext ctx = pathCache.Open())
                    {
                        foreach (Figure2D figure in path.Figures)
                        {
                            ctx.BeginFigure(ViewConverter.Convert(figure.Points[0]), filled, figure.IsClosed);
                            for (int i = 1; i < figure.Points.Count; ++i)
                            {
                                ctx.LineTo(ViewConverter.Convert(figure.Points[i]), stroked, false);
                            }
                        }
                        pathCache.Freeze();
                    }
                    AddCache(path, pathCache);
                    
                }
                yield return pathCache;
            }
        }
        private System.Boolean TryGetCache(Path2D path, out WpfStreamPath pathCache)
        {
            System.Boolean retVal = false;
            _pathCacheLock.EnterReadLock();
            try
            {
                retVal = _pathCache.TryGetValue(path, out pathCache);
            }

            finally
            {
                _pathCacheLock.ExitReadLock();
            }
            return retVal;

        }

        private void AddCache(Path2D path, WpfStreamPath pathCache)
        {
            _pathCacheLock.EnterWriteLock();
            try
            {
                _pathCache.Add(path, pathCache);
            }
            finally
            {
                _pathCacheLock.ExitWriteLock();
            }
        }
    }

    }

